/*******************************************************************************
* Copyright (c) 2011 Stephan Schwiebert. All rights reserved. This program and
* the accompanying materials are made available under the terms of the Eclipse
* Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* <p/>
* Contributors: Stephan Schwiebert - initial API and implementation
*******************************************************************************/
package org.eclipse.zest.cloudio.layout;
import java.util.Random;
import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.zest.cloudio.Word;
import org.eclipse.zest.cloudio.util.CloudMatrix;
import org.eclipse.zest.cloudio.util.RectTree;
/**
*
* @author sschwieb
*
*/
public class DefaultLayouter implements ILayouter {
public static final String X_AXIS_VARIATION = "xaxis";
public static final String Y_AXIS_VARIATION = "yaxis";
private Random random = new Random();
/**
* Percentage of x axis variation. By default, searching for free space
* is started in the center of the available area. By increasing this
* value, the start point is moved on the x axis.
*
*/
private int xAxisVariation;
/**
* Percentage of x axis variation. By default, searching for free space
* is started in the center of the available area. By increasing this
* value, the start point is moved on the y axis.
*
*/
private int yAxisVariation;
public DefaultLayouter(int i, int j) {
this.xAxisVariation = i;
this.yAxisVariation = j;
}
public Point getInitialOffset(Word word, Rectangle cloudArea) {
int xOff = 0;
if(xAxisVariation > 0) {
int range = (cloudArea.width-word.width)/200*xAxisVariation;
if(range > 0) {
xOff = random.nextInt(range);
if(random.nextBoolean()) {
xOff = -xOff;
}
}
}
int yOff = 0;
if(yAxisVariation > 0) {
int range = cloudArea.height/200*yAxisVariation;
if(range > 0) {
yOff = random.nextInt(range);
if(random.nextBoolean()) yOff = -yOff;
}
}
return new Point(xOff, yOff);
}
/**
* Tries to position the given word in the given area. First a start point is chosen,
* then the {@link RectTree} of the word and the main area is used to detect whether
* the word can be placed at the given position, or not. If not, the current point
* is moved slightly in a spiral manner, similar to the approach of Wordle.
* @param word
* @param cloudArea
* @return
*/
public boolean layout(Point offset, final Word word, final Rectangle cloudArea, CloudMatrix mainTree) {
Assert.isLegal(word != null, "Word cannot be null!");
Point next = new Point(-word.width/2, -word.height/2);
next.x += random.nextInt(25);
next.y += random.nextInt(25);
double growFactor = 1.6;
offset.x += cloudArea.width/2;
offset.y += cloudArea.height/2;
final int accuracy = mainTree.getMinResolution();
for(int i = 0; i < 5000; i++) {
final double radius = Math.sqrt((double) (next.x * next.x + next.y * next.y)) + growFactor;
double atan = Math.atan2(next.y, next.x);
if(growFactor > 1.1) {
growFactor -= 0.0007;
}
if(radius < 80) {
atan += 0.7;
} else {
atan += 20 / radius;
}
if(growFactor < 0.0005) {
growFactor = 0.0005;
}
next.x = (int) (radius * Math.cos(atan));
next.y = (int) (radius * Math.sin(atan));
word.x = ((next.x + offset.x)/accuracy)*accuracy;
word.y = ((next.y + offset.y)/accuracy)*accuracy;
RectTree rt = word.tree;
if(rt == null) break;
rt.move(word.x, word.y);
if(cloudArea.x <= word.x && cloudArea.y <= word.y && cloudArea.x+cloudArea.width >= word.x + word.width && cloudArea.y + cloudArea.height >= word.y + word.height) {
if(rt.fits(mainTree)) {
rt.place(mainTree, word.id);
return true;
}
}
}
return false;
}
public void setOption(String optionName, Object object) {
if(X_AXIS_VARIATION.equals(optionName)) {
Integer value = (Integer) object;
Assert.isLegal(value >= 0, "Parameter must be between 0 and 100 (inclusive): " + value);
Assert.isLegal(value <= 100, "Parameter must be between 0 and 100 (inclusive): " + value);
this.xAxisVariation = value;
return;
}
if(Y_AXIS_VARIATION.equals(optionName)) {
Integer value = (Integer) object;
Assert.isLegal(value >= 0, "Parameter must be between 0 and 100 (inclusive): " + value);
Assert.isLegal(value <= 100, "Parameter must be between 0 and 100 (inclusive): " + value);
this.yAxisVariation = value;
return;
}
System.err.println("Unrecognized option: " + optionName);
}
}